package com.fowo.api.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fowo.api.common.model.RException;
import com.fowo.api.sys.annotation.DataActivity;
import com.fowo.api.sys.entity.SysDepartment;
import com.fowo.api.sys.entity.SysUser;
import com.fowo.api.sys.entity.SysUserDepartment;
import com.fowo.api.sys.entity.enums.SysDataActivityAction;
import com.fowo.api.sys.mapper.SysDepartmentMapper;
import com.fowo.api.sys.mapper.SysUserDepartmentMapper;
import com.fowo.api.sys.model.SysDepartmentSearchParamPo;
import com.fowo.api.sys.model.SysDepartmentVo;
import com.fowo.api.sys.model.SysDeptUserVo;
import com.fowo.api.sys.service.SysDepartmentService;
import com.fowo.api.sys.service.SysUserService;
import com.fowo.api.user.model.CurrentUser;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysDepartmentServiceImpl extends ServiceImpl<SysDepartmentMapper, SysDepartment> implements SysDepartmentService {
    @Resource
    private SysUserDepartmentMapper userDepartmentMapper;
    @Resource
    private SysUserService userService;

    @Override
    public List<SysDepartmentVo> search(SysDepartmentSearchParamPo search) {
        search.initSort("sd.priority", "sd.");
        return baseMapper.search(search);
    }

    @Override
    public List<SysDepartmentVo> searchTree(SysDepartmentSearchParamPo search) {
        search.initSort("sd.priority", "sd.");
        List<SysDepartmentVo> departmentVos = baseMapper.search(search);
        List<SysDepartmentVo> treeVos = new ArrayList<>();
        buildTreeVo(departmentVos, treeVos, search.getParentId());
        return treeVos;
    }

    private void buildTreeVo(List<SysDepartmentVo> list, List<SysDepartmentVo> children, long parentId) {
        for (SysDepartmentVo item : list) {
            if (item.getParentId().equals(parentId)) {
                children.add(item);
                buildTreeVo(list, item.getChildren(), item.getId());
                if (item.getChildren().isEmpty()) {
                    item.setChildren(null);
                }
            }
        }
    }

    @Override
    public Page<SysDepartmentVo> pageSearch(SysDepartmentSearchParamPo search) {
        search.initSort("sd.priority", "sd.");
        return baseMapper.pageSearch(search.toPage(), search);
    }

    @DataActivity(action = SysDataActivityAction.Add)
    @Override
    public boolean create(SysDepartment sysDepartment) {
        // 父级校验与填充
        parentIdParentsFill(sysDepartment);
        // 同级名称检验
        sameNameCheck(sysDepartment);
        sysDepartment.doCreate();
        return save(sysDepartment);
    }

    private void parentIdParentsFill(SysDepartment sysDepartment) {
        if (sysDepartment.getParentId() > 0) {
            SysDepartment parent = getById(sysDepartment.getParentId());
            if (parent == null) {
                throw new RException("提供的上级部门编号无效");
            }
            List<Long> parents = new ArrayList<>(parent.getParents());
            parents.add(parent.getId());
            sysDepartment.setParents(parents);
        } else {
            sysDepartment.setParents(Collections.singletonList(0L));
        }
    }

    private void sameNameCheck(SysDepartment sysDepartment) {
        if (baseMapper.countParentIdAndName(sysDepartment.getParentId(), sysDepartment.getName()) > 0) {
            throw new RException("此层级已存在相同名称的部门");
        }
    }

    @DataActivity(action = SysDataActivityAction.Modified)
    @Transactional
    @Override
    public boolean update(SysDepartment sysDepartment) {
        SysDepartment rawSysDepartment = getById(sysDepartment.getId());
        if (rawSysDepartment == null) {
            throw new RException("此部门无效");
        }
        // 同级名称检验
        if (!sysDepartment.getName().equals(rawSysDepartment.getName())) {
            sameNameCheck(sysDepartment);
        }
        // 父级变更处理 - 重新生成 parents
        if (!sysDepartment.getParentId().equals(rawSysDepartment.getParentId())) {
            parentIdParentsFill(sysDepartment);
        }
        boolean isOk = updateById(sysDepartment);
        if (isOk) {
            // 父级变更处理 - 重新生成子孙级的 parents
            if (!sysDepartment.getParentId().equals(rawSysDepartment.getParentId())) {
                SysDepartmentSearchParamPo search = new SysDepartmentSearchParamPo();
                search.setParentId(sysDepartment.getId());
                List<SysDepartmentVo> descendants = baseMapper.search(search);
                for (SysDepartmentVo child : descendants) {
                    int curIndex = child.getParents().indexOf(sysDepartment.getId());
                    if (curIndex > 0) {
                        child.getParents().subList(0, curIndex).clear();
                    }
                    curIndex = 0;
                    for (Long parentId : sysDepartment.getParents()) {
                        child.getParents().add(curIndex++, parentId);
                    }

                    SysDepartment updateDepartment = new SysDepartment();
                    updateDepartment.setId(child.getId());
                    updateDepartment.setParents(child.getParents());
                    baseMapper.updateById(updateDepartment);
                }
            }
        }
        return isOk;
    }

    @DataActivity(action = SysDataActivityAction.Del)
    @Override
    public boolean delete(Long id) {
        final SysDepartment sysDepartment = getById(id);
        if (sysDepartment == null) {
            throw new RException("指定部门无效");
        }
        if (sysDepartment.isProtected()) {
            throw new RException("内置部门不能删除");
        }
        return super.removeById(id);
    }

    @DataActivity(action = SysDataActivityAction.View)
    @Override
    public SysDepartment getById(Long id) {
        return super.getById(id);
    }

    @Override
    public Long getIdByName(String name) {
        return baseMapper.getIdByName(name);
    }

    @Override
    public List<SysDeptUserVo> deptUserTree(SysDepartmentSearchParamPo search) {
        search.initSort("sd.priority", "sd.");
        List<SysDeptUserVo> departmentVos = baseMapper.searchUser(search);
        buildUser(departmentVos);
        List<SysDeptUserVo> treeVos = new ArrayList<>();
        buildDeptUserTreeVo(departmentVos, treeVos, search.getParentId());
        return treeVos;
    }

    private void buildUser(List<SysDeptUserVo> departmentVos) {
        if (CollectionUtils.isEmpty(departmentVos)) {
            return;
        }
        List<Long> deptId = departmentVos.stream().map(SysDeptUserVo::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(deptId)) {
            return;
        }
        LambdaQueryWrapper<SysUserDepartment> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(SysUserDepartment::getDepartmentId, deptId);
        List<SysUserDepartment> userDepartments = userDepartmentMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(userDepartments)) {
            return;
        }
        Map<Long, List<SysUserDepartment>> deptMap = userDepartments.stream().collect(Collectors.groupingBy(SysUserDepartment::getDepartmentId));
        List<Long> userIds = userDepartments.stream().map(SysUserDepartment::getUserId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(userIds)) {
            return;
        }
        List<SysUser> users = userService.queryUserList(userIds);
        Map<Long, SysUser> userMap = users.stream().collect(Collectors.toMap(SysUser::getId, user -> user));
        departmentVos.stream().forEach(item -> {
            if (!deptMap.containsKey(item.getId()) && !userMap.containsKey(deptMap.get(item.getId()))) {
                return;
            }
            List<SysUserDepartment> sysUserDepartments = deptMap.get(item.getId());
            if (CollectionUtils.isEmpty(sysUserDepartments)) {
                return;
            }
            List<CurrentUser> userList = new ArrayList<>();
            sysUserDepartments.stream().forEach(userDept -> {
                SysUser sysUser = userMap.get(userDept.getUserId());
                if (Objects.isNull(sysUser)) {
                    return;
                }
                CurrentUser user = new CurrentUser();
                user.setUserid(sysUser.getId().toString());
                user.setName(sysUser.getName());
                userList.add(user);
            });
            item.setUserVos(userList);
        });
    }

    private void buildDeptUserTreeVo(List<SysDeptUserVo> list, List<SysDeptUserVo> children, long parentId) {
        for (SysDeptUserVo item : list) {
            if (item.getParentId().equals(parentId)) {
                children.add(item);
                buildDeptUserTreeVo(list, item.getChildren(), item.getId());
                if (item.getChildren().isEmpty()) {
                    item.setChildren(null);
                }
            }
        }
    }
}